Skip to content

fix(swiftpm): actually read the artifacts version pin back - #57762

Open
chrfalch wants to merge 2 commits into
mainfrom
chrfalch/spm-read-version-pin
Open

fix(swiftpm): actually read the artifacts version pin back#57762
chrfalch wants to merge 2 commits into
mainfrom
chrfalch/spm-read-version-pin

Conversation

@chrfalch

Copy link
Copy Markdown
Collaborator

Summary:

spm add --version <ver> pinned a value that nothing ever read back.

The marker write has been there all along, and so has the reader — readArtifactsVersionOverride() in spm/generate-spm-xcodeproj.js, exported and unit-tested. It just had zero production callers. Every reference to it was a test.

determineVersion — the only resolver — went straight from the flag to package.json:

let version = args.version;      // --version
if (version == null) {
  version = pkgJson.version;     // react-native/package.json; marker never consulted
}

That value picks which artifact slots the project gets wired to. So:

  1. spm add --version 0.88.0-nightly-… → wires the nightly's slots, pins the label ✅
  2. spm update (no flag) → silently resolves package.json's version instead, re-pointing the project at different slots, while the marker still claims the nightly ❌

--version was effectively single-use. In this monorepo package.json is 1000.0.0, which has no published artifacts, so a flagless run after a pinned add fails outright — which is why the standing advice has been to pass --version on every invocation. That advice was working around this bug.

Fix: insert the pin between the two existing sources.

--version  →  pinned override  →  react-native/package.json

15 lines of logic. spm download and the scaffold path pick it up for free, since both consume the same resolved value. Because this is persistent state, one line is logged when the pin is the source, so a stale pin is diagnosable instead of silent; there is still no way to clear it short of deinit.

Three comments were actively wrong and are corrected here: the reader's doc block, the marker-field comment, and findInjectedXcodeproj's comment all asserted that the build-time sync calls this via readArtifactsVersionOverride. It does not and never did — the sync action returns before artifacts are resolved at all. Those comments are what made the gap invisible; they misled me while investigating.

Changelog:

[Internal] [Fixed] - SwiftPM: spm --version now sticks, so a later run without the flag keeps using the pinned artifact version

Test Plan:

yarn jest packages/react-native/scripts31 suites, 684 tests, all green.

New tests, written red first — the load-bearing one failed with Expected: "0.80.0" / Received: "1000.0.0", i.e. exactly the reported bug:

  • --version given → wins, even with a different value pinned
  • no flag, pin present → the pinned version is used
  • no flag, no pin → package.json's version (unchanged behaviour)
  • no flag, corrupt or absent marker → package.json's version, no throw
  • the log line fires only when the pin is the source

The three fallback cases passed before the fix too, which is the point — they pin today's behaviour so this change can't regress it.

Note on landing order

Touches setup-apple-spm.js and spm/generate-spm-xcodeproj.js, which #57744, #57756 and #57757 also touch. All are cut independently from main; whichever lands first, I'll rebase the rest. #57756 is the closest relative — it does the same wiring for --config-command, which had the identical write-only-pin shape.

`spm add --version <ver>` / `spm update --version <ver>` already wrote an
`artifactsVersionOverride` into the `.spm-injected.json` marker, and
`readArtifactsVersionOverride` already existed to read it back — but nothing
ever called it. Only the write half was wired; every reference to the reader
was a test.

The sole resolver, determineVersion, went straight from the `--version` flag to
node_modules/react-native/package.json. So after `spm add --version X`, a later
flagless `spm update` silently re-pointed the project at package.json's version
instead, while the marker went on claiming X. `--version` was effectively
single-use. In this monorepo package.json is 1000.0.0, which has no published
artifacts, so a flagless run after a pinned add fails outright — hence the
standing advice to pass `--version` on every invocation.

Insert the pin between the two existing sources:

    --version  ->  pinned override  ->  react-native/package.json

`spm download` and the scaffold path pick it up for free, since both use the
same resolved value. Since this is persistent state, log one line when the pin
is the source, so a stale pin is diagnosable rather than silent; there is still
no way to clear it short of `deinit`.

Three comments claimed the build-time sync read this override via
readArtifactsVersionOverride. It does not, and never did — the sync action
returns before artifacts are resolved at all. They now name the real consumer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 30, 2026
@chrfalch
chrfalch requested review from cipolleschi and Copilot and removed request for Copilot July 30, 2026 09:04
@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 30, 2026
@chrfalch chrfalch changed the title SPM: actually read the artifacts version pin back fix(swiftpm): actually read the artifacts version pin back Jul 30, 2026
## Summary:

The SwiftPM docs still described `--version` as "RN version (default: from
package.json)", which stopped being true once determineVersion started reading
the `artifactsVersionOverride` pin back out of `.spm-injected.json`. Document
the real resolution order and why it matters:

- The `--version` row in "CLI Options" now names all three sources in order —
  the flag, the pin a previous `--version` wrote into the marker, then
  node_modules/react-native/package.json — and says you pass it once.
- A new "Pinning the React Native version" subsection explains the failure the
  pin prevents, which is not self-evident: the resolved version selects which
  artifact slots the project is wired to, so a flagless run falling back to
  package.json re-points the project at different slots while the marker still
  advertises the pinned one. It also states that `deinit` drops the marker and
  with it the pin, so a later `add` resolves package.json again unless you pass
  `--version`.
- The `.spm-injected.json` row in "What to commit" described the marker only as
  a record of the edits `add` made. One appended sentence makes its second role
  visible; the row is otherwise untouched, since sibling PRs edit it too.

No advice to repeat `--version` on every invocation existed in the docs to
correct — that workaround was only ever passed on verbally.

The file is not Prettier-formatted on this branch, so it was deliberately not
reformatted: prose stays wrapped near 80 columns and table rows stay on one
line, matching the surrounding style.

## Changelog:

[Internal] - Document the `spm --version` pin and its resolution order

## Test Plan:

Docs-only change; no code or tests touched. Verified against the branch's own
diff that the marker key is `artifactsVersionOverride` (read by
`readArtifactsVersionOverride` in scripts/spm/generate-spm-xcodeproj.js) and
that `determineVersion` in scripts/setup-apple-spm.js resolves flag → pin →
node_modules/react-native/package.json.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant